{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to Automate Data Cleaning with AI <a id=\"make-a-data-cleaning-agent\"></a>\n",
    "\n",
    "In this tutorial, you will learn how to automate data cleaning with AI. It can automatically:\n",
    "\n",
    "- detect and fix common data cleaning issues\n",
    "- missing values\n",
    "- duplicate rows\n",
    "- inconsistent data types. \n",
    " \n",
    "By using this AI agent, you can save time and effort on data cleaning, allowing you to focus on more important tasks.\n",
    "\n",
    "### Want To Become A Full-Stack Generative AI Data Scientist?\n",
    "\n",
    "![Generative AI Data Scientist](../img/become_a_generative_ai_data_scientist.jpg)\n",
    "\n",
    "I teach Generative AI Data Science to help you build AI-powered data science apps. [**Register for my next Generative AI for Data Scientists workshop here.**](https://learn.business-science.io/ai-register)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "bat"
    }
   },
   "source": [
    "# Table of Contents\n",
    "\n",
    "1. [Make A Data Cleaning Agent](#make-a-data-cleaning-agent)\n",
    "2. [Load Libraries](#load-libraries)\n",
    "3. [Setup AI and Logging](#setup-ai-and-logging)\n",
    "4. [Load a Dataset](#load-a-dataset)\n",
    "5. [Create The Agent](#create-the-agent)\n",
    "6. [Response](#response)\n",
    "7. [The cleaning recipe](#the-cleaning-recipe)\n",
    "8. [Data Cleaner Function](#data-cleaner-function)\n",
    "9. [Cleaned Data As Pandas Data Frame](#cleaned-data-as-pandas-data-frame)\n",
    "10. [Free Generative AI Data Science Workshop](#free-generative-ai-data-science-workshop)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load Libraries <a id=\"load-libraries\"></a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# * Libraries\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "import os\n",
    "import yaml\n",
    "import pandas as pd\n",
    "from pprint import pprint\n",
    "\n",
    "from ai_data_science_team.agents import DataCleaningAgent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup AI and Logging <a id=\"setup-ai-and-logging\"></a>\n",
    "\n",
    "This section of code sets up the LLM inputs and the logging information. Logging is used to store AI-generated code and files during the AI Data Science Teams processing of files. \n",
    "\n",
    "*Important Note:* This example uses OpenAI's API. But any LLM can be used such as Anthropic or local LLMs with Ollama."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatOpenAI(client=<openai.resources.chat.completions.Completions object at 0x7fdb3903cca0>, async_client=<openai.resources.chat.completions.AsyncCompletions object at 0x7fdb3903f370>, root_client=<openai.OpenAI object at 0x7fdb48959570>, root_async_client=<openai.AsyncOpenAI object at 0x7fdb3903cc10>, model_name='gpt-4o-mini', model_kwargs={}, openai_api_key=SecretStr('**********'))"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# * Setup\n",
    "\n",
    "MODEL    = \"gpt-4o-mini\"\n",
    "LOG      = True\n",
    "LOG_PATH = os.path.join(os.getcwd(), \"logs/\")\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = yaml.safe_load(open('../credentials.yml'))['openai']\n",
    "\n",
    "llm = ChatOpenAI(model = MODEL)\n",
    "\n",
    "llm\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load a Dataset <a id=\"load-a-dataset\"></a>\n",
    "\n",
    "Next, let's load a customer churn data set that we will clean up. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>customerID</th>\n",
       "      <th>gender</th>\n",
       "      <th>SeniorCitizen</th>\n",
       "      <th>Partner</th>\n",
       "      <th>Dependents</th>\n",
       "      <th>tenure</th>\n",
       "      <th>PhoneService</th>\n",
       "      <th>MultipleLines</th>\n",
       "      <th>InternetService</th>\n",
       "      <th>OnlineSecurity</th>\n",
       "      <th>...</th>\n",
       "      <th>DeviceProtection</th>\n",
       "      <th>TechSupport</th>\n",
       "      <th>StreamingTV</th>\n",
       "      <th>StreamingMovies</th>\n",
       "      <th>Contract</th>\n",
       "      <th>PaperlessBilling</th>\n",
       "      <th>PaymentMethod</th>\n",
       "      <th>MonthlyCharges</th>\n",
       "      <th>TotalCharges</th>\n",
       "      <th>Churn</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>7590-VHVEG</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>1</td>\n",
       "      <td>No</td>\n",
       "      <td>No phone service</td>\n",
       "      <td>DSL</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Electronic check</td>\n",
       "      <td>29.85</td>\n",
       "      <td>29.85</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>5575-GNVDE</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>34</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>One year</td>\n",
       "      <td>No</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>56.95</td>\n",
       "      <td>1889.5</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3668-QPYBK</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>2</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>53.85</td>\n",
       "      <td>108.15</td>\n",
       "      <td>Yes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>7795-CFOCW</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>45</td>\n",
       "      <td>No</td>\n",
       "      <td>No phone service</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>One year</td>\n",
       "      <td>No</td>\n",
       "      <td>Bank transfer (automatic)</td>\n",
       "      <td>42.30</td>\n",
       "      <td>1840.75</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>9237-HQITU</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>2</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Electronic check</td>\n",
       "      <td>70.70</td>\n",
       "      <td>151.65</td>\n",
       "      <td>Yes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7038</th>\n",
       "      <td>6840-RESVB</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>24</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>One year</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>84.80</td>\n",
       "      <td>1990.5</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7039</th>\n",
       "      <td>2234-XADUH</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>72</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>One year</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Credit card (automatic)</td>\n",
       "      <td>103.20</td>\n",
       "      <td>7362.9</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7040</th>\n",
       "      <td>4801-JZAZL</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>11</td>\n",
       "      <td>No</td>\n",
       "      <td>No phone service</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Electronic check</td>\n",
       "      <td>29.60</td>\n",
       "      <td>346.45</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7041</th>\n",
       "      <td>8361-LTMKD</td>\n",
       "      <td>Male</td>\n",
       "      <td>1</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>4</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>74.40</td>\n",
       "      <td>306.6</td>\n",
       "      <td>Yes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7042</th>\n",
       "      <td>3186-AJIEK</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>66</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Two year</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Bank transfer (automatic)</td>\n",
       "      <td>105.65</td>\n",
       "      <td>6844.5</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>7043 rows × 21 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      customerID  gender  SeniorCitizen Partner Dependents  tenure  \\\n",
       "0     7590-VHVEG  Female              0     Yes         No       1   \n",
       "1     5575-GNVDE    Male              0      No         No      34   \n",
       "2     3668-QPYBK    Male              0      No         No       2   \n",
       "3     7795-CFOCW    Male              0      No         No      45   \n",
       "4     9237-HQITU  Female              0      No         No       2   \n",
       "...          ...     ...            ...     ...        ...     ...   \n",
       "7038  6840-RESVB    Male              0     Yes        Yes      24   \n",
       "7039  2234-XADUH  Female              0     Yes        Yes      72   \n",
       "7040  4801-JZAZL  Female              0     Yes        Yes      11   \n",
       "7041  8361-LTMKD    Male              1     Yes         No       4   \n",
       "7042  3186-AJIEK    Male              0      No         No      66   \n",
       "\n",
       "     PhoneService     MultipleLines InternetService OnlineSecurity  ...  \\\n",
       "0              No  No phone service             DSL             No  ...   \n",
       "1             Yes                No             DSL            Yes  ...   \n",
       "2             Yes                No             DSL            Yes  ...   \n",
       "3              No  No phone service             DSL            Yes  ...   \n",
       "4             Yes                No     Fiber optic             No  ...   \n",
       "...           ...               ...             ...            ...  ...   \n",
       "7038          Yes               Yes             DSL            Yes  ...   \n",
       "7039          Yes               Yes     Fiber optic             No  ...   \n",
       "7040           No  No phone service             DSL            Yes  ...   \n",
       "7041          Yes               Yes     Fiber optic             No  ...   \n",
       "7042          Yes                No     Fiber optic            Yes  ...   \n",
       "\n",
       "     DeviceProtection TechSupport StreamingTV StreamingMovies        Contract  \\\n",
       "0                  No          No          No              No  Month-to-month   \n",
       "1                 Yes          No          No              No        One year   \n",
       "2                  No          No          No              No  Month-to-month   \n",
       "3                 Yes         Yes          No              No        One year   \n",
       "4                  No          No          No              No  Month-to-month   \n",
       "...               ...         ...         ...             ...             ...   \n",
       "7038              Yes         Yes         Yes             Yes        One year   \n",
       "7039              Yes          No         Yes             Yes        One year   \n",
       "7040               No          No          No              No  Month-to-month   \n",
       "7041               No          No          No              No  Month-to-month   \n",
       "7042              Yes         Yes         Yes             Yes        Two year   \n",
       "\n",
       "     PaperlessBilling              PaymentMethod MonthlyCharges  TotalCharges  \\\n",
       "0                 Yes           Electronic check          29.85         29.85   \n",
       "1                  No               Mailed check          56.95        1889.5   \n",
       "2                 Yes               Mailed check          53.85        108.15   \n",
       "3                  No  Bank transfer (automatic)          42.30       1840.75   \n",
       "4                 Yes           Electronic check          70.70        151.65   \n",
       "...               ...                        ...            ...           ...   \n",
       "7038              Yes               Mailed check          84.80        1990.5   \n",
       "7039              Yes    Credit card (automatic)         103.20        7362.9   \n",
       "7040              Yes           Electronic check          29.60        346.45   \n",
       "7041              Yes               Mailed check          74.40         306.6   \n",
       "7042              Yes  Bank transfer (automatic)         105.65        6844.5   \n",
       "\n",
       "     Churn  \n",
       "0       No  \n",
       "1       No  \n",
       "2      Yes  \n",
       "3       No  \n",
       "4      Yes  \n",
       "...    ...  \n",
       "7038    No  \n",
       "7039    No  \n",
       "7040    No  \n",
       "7041   Yes  \n",
       "7042    No  \n",
       "\n",
       "[7043 rows x 21 columns]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = pd.read_csv(\"https://raw.githubusercontent.com/business-science/ai-data-science-team/refs/heads/master/data/churn_data.csv\")\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create The Agent <a id=\"create-the-agent\"></a>\n",
    "\n",
    "Run this code to create an agent with `make_data_cleaning_agent()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<ai_data_science_team.agents.data_cleaning_agent.DataCleaningAgent object at 0x7fdb3903c040>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_cleaning_agent = DataCleaningAgent(\n",
    "    model = llm, \n",
    "    log=LOG, \n",
    "    log_path=LOG_PATH\n",
    ")\n",
    "\n",
    "data_cleaning_agent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This creates an `app`, which is a langgraph agent with the main inputs:\n",
    "\n",
    "- **user_instructions**: The data cleaning agent will use these comments to modify the \"standard recipe\" \n",
    "  - Standard Recipe: The standard cleaning recipe which includes removing columns with more than 40% missing values, imputing missing values using mean (numeric) or mode (categorical), removing duplicate rows, and removing outliers. \n",
    "- **data_raw**: The raw data to be cleaned\n",
    "- **max_retries**: Used to limit the number of attempts to fix the python code generated by the agent. Set this to 3 to limit to 3 attempts. \n",
    "- **retry_count**: Set this to 0. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---DATA CLEANING AGENT----\n",
      "    * RECOMMEND CLEANING STEPS\n",
      "    * CREATE DATA CLEANER CODE\n",
      "      File saved to: /Users/mdancho/Desktop/course_code/ai-data-science-team/logs/data_cleaner.py\n",
      "    * EXECUTING AGENT CODE\n",
      "No missing values found.\n",
      "Unique values for MultipleLines: ['No phone service' 'No' 'Yes']\n",
      "Unique values for InternetService: ['DSL' 'Fiber optic' 'No']\n",
      "Unique values for OnlineSecurity: ['No' 'Yes' 'No internet service']\n",
      "Unique values for OnlineBackup: ['Yes' 'No' 'No internet service']\n",
      "Unique values for DeviceProtection: ['No' 'Yes' 'No internet service']\n",
      "Unique values for TechSupport: ['No' 'Yes' 'No internet service']\n",
      "Unique values for StreamingTV: ['No' 'Yes' 'No internet service']\n",
      "Unique values for StreamingMovies: ['No' 'Yes' 'No internet service']\n",
      "Detected 0 outliers in MonthlyCharges.\n",
      "Detected 0 outliers in TotalCharges.\n",
      "Data cleaning completed. Review the logged outputs for any issues found during cleaning.\n",
      "Final dataset shape: (7043, 21)\n",
      "<class 'pandas.core.frame.DataFrame'>\n",
      "Index: 7043 entries, 0 to 7042\n",
      "Data columns (total 21 columns):\n",
      " #   Column            Non-Null Count  Dtype  \n",
      "---  ------            --------------  -----  \n",
      " 0   customerID        7043 non-null   object \n",
      " 1   gender            7043 non-null   object \n",
      " 2   SeniorCitizen     7043 non-null   int64  \n",
      " 3   Partner           7043 non-null   object \n",
      " 4   Dependents        7043 non-null   object \n",
      " 5   tenure            7043 non-null   int64  \n",
      " 6   PhoneService      7043 non-null   object \n",
      " 7   MultipleLines     7043 non-null   object \n",
      " 8   InternetService   7043 non-null   object \n",
      " 9   OnlineSecurity    7043 non-null   object \n",
      " 10  OnlineBackup      7043 non-null   object \n",
      " 11  DeviceProtection  7043 non-null   object \n",
      " 12  TechSupport       7043 non-null   object \n",
      " 13  StreamingTV       7043 non-null   object \n",
      " 14  StreamingMovies   7043 non-null   object \n",
      " 15  Contract          7043 non-null   object \n",
      " 16  PaperlessBilling  7043 non-null   object \n",
      " 17  PaymentMethod     7043 non-null   object \n",
      " 18  MonthlyCharges    7043 non-null   float64\n",
      " 19  TotalCharges      7032 non-null   float64\n",
      " 20  Churn             7043 non-null   object \n",
      "dtypes: float64(2), int64(2), object(17)\n",
      "memory usage: 1.2+ MB\n",
      "None\n",
      "    * EXPLAIN AGENT CODE\n"
     ]
    }
   ],
   "source": [
    "data_cleaning_agent.invoke_agent(\n",
    "    data_raw=df,\n",
    "    user_instructions=\"Don't remove outliers when cleaning the data.\",\n",
    "    max_retries=3,\n",
    "    retry_count=0\n",
    ")  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Response <a id=\"response\"></a>\n",
    "\n",
    "The response produced contains everything we need to understand the data cleaning decisions made and get the cleaned dataset. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['messages',\n",
       " 'user_instructions',\n",
       " 'recommended_steps',\n",
       " 'data_raw',\n",
       " 'data_cleaned',\n",
       " 'all_datasets_summary',\n",
       " 'data_cleaner_function',\n",
       " 'data_cleaner_function_path',\n",
       " 'data_cleaner_function_name',\n",
       " 'data_cleaner_error',\n",
       " 'max_retries',\n",
       " 'retry_count']"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = data_cleaning_agent.get_response()\n",
    "\n",
    "list(response.keys())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Cleaned Data As Pandas Data Frame <a id=\"cleaned-data-as-pandas-data-frame\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use the `get_data_cleaned()` method to get the cleaned data as a pandas data frame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>customerID</th>\n",
       "      <th>gender</th>\n",
       "      <th>SeniorCitizen</th>\n",
       "      <th>Partner</th>\n",
       "      <th>Dependents</th>\n",
       "      <th>tenure</th>\n",
       "      <th>PhoneService</th>\n",
       "      <th>MultipleLines</th>\n",
       "      <th>InternetService</th>\n",
       "      <th>OnlineSecurity</th>\n",
       "      <th>...</th>\n",
       "      <th>DeviceProtection</th>\n",
       "      <th>TechSupport</th>\n",
       "      <th>StreamingTV</th>\n",
       "      <th>StreamingMovies</th>\n",
       "      <th>Contract</th>\n",
       "      <th>PaperlessBilling</th>\n",
       "      <th>PaymentMethod</th>\n",
       "      <th>MonthlyCharges</th>\n",
       "      <th>TotalCharges</th>\n",
       "      <th>Churn</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>7590-VHVEG</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>1</td>\n",
       "      <td>No</td>\n",
       "      <td>No phone service</td>\n",
       "      <td>DSL</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Electronic check</td>\n",
       "      <td>29.85</td>\n",
       "      <td>29.85</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>5575-GNVDE</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>34</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>One year</td>\n",
       "      <td>No</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>56.95</td>\n",
       "      <td>1889.50</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3668-QPYBK</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>2</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>53.85</td>\n",
       "      <td>108.15</td>\n",
       "      <td>Yes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>7795-CFOCW</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>45</td>\n",
       "      <td>No</td>\n",
       "      <td>No phone service</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>One year</td>\n",
       "      <td>No</td>\n",
       "      <td>Bank transfer (automatic)</td>\n",
       "      <td>42.30</td>\n",
       "      <td>1840.75</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>9237-HQITU</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>2</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Electronic check</td>\n",
       "      <td>70.70</td>\n",
       "      <td>151.65</td>\n",
       "      <td>Yes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7038</th>\n",
       "      <td>6840-RESVB</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>24</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>One year</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>84.80</td>\n",
       "      <td>1990.50</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7039</th>\n",
       "      <td>2234-XADUH</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>72</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>One year</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Credit card (automatic)</td>\n",
       "      <td>103.20</td>\n",
       "      <td>7362.90</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7040</th>\n",
       "      <td>4801-JZAZL</td>\n",
       "      <td>Female</td>\n",
       "      <td>0</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>11</td>\n",
       "      <td>No</td>\n",
       "      <td>No phone service</td>\n",
       "      <td>DSL</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Electronic check</td>\n",
       "      <td>29.60</td>\n",
       "      <td>346.45</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7041</th>\n",
       "      <td>8361-LTMKD</td>\n",
       "      <td>Male</td>\n",
       "      <td>1</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>4</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>No</td>\n",
       "      <td>...</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>Month-to-month</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Mailed check</td>\n",
       "      <td>74.40</td>\n",
       "      <td>306.60</td>\n",
       "      <td>Yes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7042</th>\n",
       "      <td>3186-AJIEK</td>\n",
       "      <td>Male</td>\n",
       "      <td>0</td>\n",
       "      <td>No</td>\n",
       "      <td>No</td>\n",
       "      <td>66</td>\n",
       "      <td>Yes</td>\n",
       "      <td>No</td>\n",
       "      <td>Fiber optic</td>\n",
       "      <td>Yes</td>\n",
       "      <td>...</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Two year</td>\n",
       "      <td>Yes</td>\n",
       "      <td>Bank transfer (automatic)</td>\n",
       "      <td>105.65</td>\n",
       "      <td>6844.50</td>\n",
       "      <td>No</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>7043 rows × 21 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      customerID  gender  SeniorCitizen Partner Dependents  tenure  \\\n",
       "0     7590-VHVEG  Female              0     Yes         No       1   \n",
       "1     5575-GNVDE    Male              0      No         No      34   \n",
       "2     3668-QPYBK    Male              0      No         No       2   \n",
       "3     7795-CFOCW    Male              0      No         No      45   \n",
       "4     9237-HQITU  Female              0      No         No       2   \n",
       "...          ...     ...            ...     ...        ...     ...   \n",
       "7038  6840-RESVB    Male              0     Yes        Yes      24   \n",
       "7039  2234-XADUH  Female              0     Yes        Yes      72   \n",
       "7040  4801-JZAZL  Female              0     Yes        Yes      11   \n",
       "7041  8361-LTMKD    Male              1     Yes         No       4   \n",
       "7042  3186-AJIEK    Male              0      No         No      66   \n",
       "\n",
       "     PhoneService     MultipleLines InternetService OnlineSecurity  ...  \\\n",
       "0              No  No phone service             DSL             No  ...   \n",
       "1             Yes                No             DSL            Yes  ...   \n",
       "2             Yes                No             DSL            Yes  ...   \n",
       "3              No  No phone service             DSL            Yes  ...   \n",
       "4             Yes                No     Fiber optic             No  ...   \n",
       "...           ...               ...             ...            ...  ...   \n",
       "7038          Yes               Yes             DSL            Yes  ...   \n",
       "7039          Yes               Yes     Fiber optic             No  ...   \n",
       "7040           No  No phone service             DSL            Yes  ...   \n",
       "7041          Yes               Yes     Fiber optic             No  ...   \n",
       "7042          Yes                No     Fiber optic            Yes  ...   \n",
       "\n",
       "     DeviceProtection TechSupport StreamingTV StreamingMovies        Contract  \\\n",
       "0                  No          No          No              No  Month-to-month   \n",
       "1                 Yes          No          No              No        One year   \n",
       "2                  No          No          No              No  Month-to-month   \n",
       "3                 Yes         Yes          No              No        One year   \n",
       "4                  No          No          No              No  Month-to-month   \n",
       "...               ...         ...         ...             ...             ...   \n",
       "7038              Yes         Yes         Yes             Yes        One year   \n",
       "7039              Yes          No         Yes             Yes        One year   \n",
       "7040               No          No          No              No  Month-to-month   \n",
       "7041               No          No          No              No  Month-to-month   \n",
       "7042              Yes         Yes         Yes             Yes        Two year   \n",
       "\n",
       "     PaperlessBilling              PaymentMethod MonthlyCharges  TotalCharges  \\\n",
       "0                 Yes           Electronic check          29.85         29.85   \n",
       "1                  No               Mailed check          56.95       1889.50   \n",
       "2                 Yes               Mailed check          53.85        108.15   \n",
       "3                  No  Bank transfer (automatic)          42.30       1840.75   \n",
       "4                 Yes           Electronic check          70.70        151.65   \n",
       "...               ...                        ...            ...           ...   \n",
       "7038              Yes               Mailed check          84.80       1990.50   \n",
       "7039              Yes    Credit card (automatic)         103.20       7362.90   \n",
       "7040              Yes           Electronic check          29.60        346.45   \n",
       "7041              Yes               Mailed check          74.40        306.60   \n",
       "7042              Yes  Bank transfer (automatic)         105.65       6844.50   \n",
       "\n",
       "      Churn  \n",
       "0        No  \n",
       "1        No  \n",
       "2       Yes  \n",
       "3        No  \n",
       "4       Yes  \n",
       "...     ...  \n",
       "7038     No  \n",
       "7039     No  \n",
       "7040     No  \n",
       "7041    Yes  \n",
       "7042     No  \n",
       "\n",
       "[7043 rows x 21 columns]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_cleaning_agent.get_data_cleaned()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Data Cleaner Function <a id=\"data-cleaner-function\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use the `get_data_cleaner_function()` method to get the data cleaner function pipeline. \n",
    "\n",
    "- In Jupyter Notebooks, setting `markdown=True` will return the function as markdown code. \n",
    "- In Streamlit apps, it's recommended to set `markdown=False`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```python\n",
       "# Disclaimer: This function was generated by AI. Please review before using.\n",
       "# Agent Name: data_cleaning_agent\n",
       "# Time Created: 2025-01-10 08:07:31\n",
       "\n",
       "def data_cleaner(data_raw):\n",
       "    import pandas as pd\n",
       "    import numpy as np\n",
       "\n",
       "\n",
       "    # Step 1: Check for Missing Values\n",
       "    if data_raw.isnull().sum().sum() == 0:\n",
       "        print(\"No missing values found.\")\n",
       "    else:\n",
       "        print(\"Missing values detected!\")\n",
       "\n",
       "    # Step 2: Convert Data Types\n",
       "    data_raw['TotalCharges'] = pd.to_numeric(data_raw['TotalCharges'], errors='coerce')\n",
       "\n",
       "    # Step 3: Remove Duplicate Rows\n",
       "    data_cleaned = data_raw.drop_duplicates()\n",
       "\n",
       "    # Step 4: Analyze Data for Additional Cleaning Needs\n",
       "    categorical_columns = [\n",
       "        'MultipleLines', 'InternetService', 'OnlineSecurity', \n",
       "        'OnlineBackup', 'DeviceProtection', 'TechSupport', \n",
       "        'StreamingTV', 'StreamingMovies'\n",
       "    ]\n",
       "\n",
       "    for col in categorical_columns:\n",
       "        # Remove leading/trailing whitespace\n",
       "        data_cleaned[col] = data_cleaned[col].str.strip()\n",
       "\n",
       "    # Check unique value counts for categorical variables\n",
       "    for col in categorical_columns:\n",
       "        unique_values = data_cleaned[col].unique()\n",
       "        print(f\"Unique values for {col}: {unique_values}\")\n",
       "\n",
       "    # Step 5: Check for Extreme Outliers\n",
       "    # Here we can use a simple standard deviation method or IQR to identify outliers\n",
       "    outlier_thresholds = {\n",
       "        'MonthlyCharges': (data_cleaned['MonthlyCharges'].mean() + 3 * data_cleaned['MonthlyCharges'].std(), \n",
       "                           data_cleaned['MonthlyCharges'].mean() - 3 * data_cleaned['MonthlyCharges'].std()),\n",
       "        'TotalCharges': (data_cleaned['TotalCharges'].mean() + 3 * data_cleaned['TotalCharges'].std(), \n",
       "                         data_cleaned['TotalCharges'].mean() - 3 * data_cleaned['TotalCharges'].std())\n",
       "    }\n",
       "    \n",
       "    for key, value in outlier_thresholds.items():\n",
       "        outliers = data_cleaned[(data_cleaned[key] > value[0]) | (data_cleaned[key] < value[1])]\n",
       "        print(f\"Detected {len(outliers)} outliers in {key}.\")\n",
       "\n",
       "    # Step 6: Log Findings\n",
       "    print(\"Data cleaning completed. Review the logged outputs for any issues found during cleaning.\")\n",
       "\n",
       "    # Step 7: Final Review\n",
       "    print(f\"Final dataset shape: {data_cleaned.shape}\")\n",
       "    print(data_cleaned.info())\n",
       "\n",
       "    return data_cleaned\n",
       "```"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_cleaning_agent.get_data_cleaner_function(markdown=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Recommended Steps\n",
    "\n",
    "To get the recommended steps during the data analysis (prior to coding), run the `get_recommended_steps()` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "\n",
       "\n",
       "# Recommended Data Cleaning Steps:\n",
       "1. **Check for Missing Values**: Although there are no missing values in the dataset, confirm that each column is accounted for and has complete data.\n",
       "\n",
       "2. **Convert Data Types**: \n",
       "   - Convert `TotalCharges` from `object` to `float64`, as it should represent numerical values for analysis.\n",
       "\n",
       "3. **Remove Duplicate Rows**: Check for and remove any duplicate rows to ensure data integrity. \n",
       "\n",
       "4. **Analyze Data for Additional Cleaning Needs**: \n",
       "   - Inspect the categorical variables to ensure they are formatted consistently (e.g., check for leading/trailing spaces in string fields).\n",
       "   - Ensure all categorical variables have a manageable number of unique values, particularly for the `MultipleLines`, `InternetService`, `OnlineSecurity`, `OnlineBackup`, `DeviceProtection`, `TechSupport`, `StreamingTV`, and `StreamingMovies` columns.\n",
       "\n",
       "5. **Check for Extreme Outliers**: Although outlier removal is not required per user instructions, it is still beneficial to identify any extreme outliers for awareness in subsequent analyses. \n",
       "\n",
       "6. **Log Findings**: Document any observations made during the cleaning process for future reference or for further analysis.\n",
       "\n",
       "7. **Final Review**: Conduct a final review of the dataset to confirm all cleaning steps have been successfully implemented without any issues."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_cleaning_agent.get_recommended_cleaning_steps(markdown=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Want To Become A Full-Stack Generative AI Data Scientist?\n",
    "\n",
    "![Generative AI Data Scientist](../img/become_a_generative_ai_data_scientist.jpg)\n",
    "\n",
    "I teach Generative AI Data Science to help you build AI-powered data science apps. [**Register for my next Generative AI for Data Scientists workshop here.**](https://learn.business-science.io/ai-register)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ds4b_301p_dev",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
